package de.unikoblenz.west.csxpoi;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import de.unikoblenz.west.csxpoi.R;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;

/**
 * The activity for searching POIs.
 */
public class SearchActivity extends Activity {

	/**
	 * the constant representing the searching dialog
	 */
	private static final int SEARCHING_DIALOG = 1;

	/**
	 * Called when the activity is started.
	 */
	@Override
	protected void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);

		requestWindowFeature(Window.FEATURE_NO_TITLE);

		Bundle extras = getIntent().getExtras();

		String userId = extras.getString("user");

		if (userId != null) {

			// TODO: hide view completely

			new AsyncSearch().execute(userId);

		} else {

			setContentView(R.layout.search);

			final String left = String.valueOf(extras.getDouble("left"));
			final String right = String.valueOf(extras.getDouble("right"));
			final String top = String.valueOf(extras.getDouble("top"));
			final String bottom = String.valueOf(extras.getDouble("bottom"));

			// Cancel button:
			final Button cancelButton = (Button) findViewById(R.id.cancel_button);
			cancelButton.setOnClickListener(new View.OnClickListener() {
				public void onClick(View v) {
					finish();
				}
			});

			// Search button:
			final Button searchButton = (Button) findViewById(R.id.search_button);
			final EditText queryView = (EditText) findViewById(R.id.query);
			searchButton.setOnClickListener(new View.OnClickListener() {
				public void onClick(View v) {
					String query = queryView.getText().toString().trim();
					new AsyncSearch().execute(query, left, right, top, bottom);
				}
			});

			// All POIs button:
			final Button allPoisButton = (Button) findViewById(R.id.all_pois_button);
			allPoisButton.setOnClickListener(new View.OnClickListener() {
				public void onClick(View v) {
					new AsyncSearch().execute("", left, right, top, bottom);
				}
			});			
		}
	}

	/**
	 * Called when a dialog is shown.
	 */
	@Override
	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case SEARCHING_DIALOG:
			ProgressDialog waitAPIDialog = new ProgressDialog(this);
			waitAPIDialog.setTitle(R.string.searching_dialog_title);
			waitAPIDialog.setMessage(getResources().getString(
					R.string.please_wait_text));
			waitAPIDialog.setIndeterminate(true);
			waitAPIDialog.setCancelable(true);
//			System.out.println("Showing Search dialog");
			return waitAPIDialog;
		}
		return super.onCreateDialog(id);
	}

	/**
	 * Asynchronously searches POIs.
	 */
	private class AsyncSearch extends AsyncTask<String, Void, PoiWrapper[]> {
		
		/**
		 * Called before the asynchronous search.
		 */
		@Override
		protected void onPreExecute() {
			showDialog(SEARCHING_DIALOG);
			dismissDialog(SEARCHING_DIALOG);
		}

		/**
		 * Called when starting the asynchronous search.
		 */
		@Override
		protected PoiWrapper[] doInBackground(String... args) {
			try {
				URL url = null;
				if (args.length == 1) {
					String userId = args[0];
					url = new URL(Constants.REST_ADDRESS + "pois" + "?user="
							+ Encoder.urlEncode(userId));
				} else if (args.length == 5) {
					String query = args[0];
					String left = args[1];
					String right = args[2];
					String top = args[3];
					String bottom = args[4];
					url = new URL(Constants.REST_ADDRESS + "pois" + "?query="
							+ Encoder.urlEncode(query) + "&left=" + left
							+ "&right=" + right + "&top=" + top + "&bottom="
							+ bottom);
				}
				if (url != null) {
					SAXParserFactory spf = SAXParserFactory.newInstance();
					SAXParser sp = spf.newSAXParser();
					XMLReader xr = sp.getXMLReader();
					SearchSaxHandler searchSaxHandler = new SearchSaxHandler();
					xr.setContentHandler(searchSaxHandler);
					xr.parse(new InputSource(url.openStream()));
					List<PoiWrapper> pois = searchSaxHandler.getList();
					return pois.toArray(new PoiWrapper[pois.size()]);
				}
			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (ParserConfigurationException e) {
				e.printStackTrace();
			} catch (SAXException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return null;
		}

		/**
		 * Called after the asynchronous search.
		 */
		@Override
		protected void onPostExecute(PoiWrapper[] pois) {
			dismissDialog(SEARCHING_DIALOG);
			Intent resultIntent = new Intent(SearchActivity.this,
					CsxPoiActivity.class);
			if (pois != null) {
				resultIntent.putExtra("pois", pois);
				setResult(RESULT_OK, resultIntent);
			} else {
				setResult(RESULT_CANCELED, resultIntent);
			}
			finish();
		}
	}

	/**
	 * Parses the XML response from the POIs REST request.
	 */
	private class SearchSaxHandler extends DefaultHandler {

		/**
		 * the list of matching POIs
		 */
		private List<PoiWrapper> mPois = new ArrayList<PoiWrapper>();

		/**
		 * the current POI
		 */
		private PoiWrapper mCurrentPoi = null;

		/**
		 * the current category of the current POI
		 */
		private CategoryWrapper mCurrentCategory = null;

		/**
		 * the characters of the current tag
		 */
		private String mCurrentCharacters = "";

		/**
		 * the constant for the poi tag
		 */
		private static final String POI_TAG = "poi";

		/**
		 * the constant for the name tag
		 */
		private static final String NAME_TAG = "name";

		/**
		 * the constant for the lat tag
		 */
		private static final String LAT_TAG = "lat";

		/**
		 * the constant for the long tag
		 */
		private static final String LONG_TAG = "long";

		/**
		 * the constant for the alt tag
		 */
		private static final String ALT_TAG = "alt";

		/**
		 * the constant for the category tag
		 */
		private static final String CATEGORY_TAG = "category";

		/**
		 * the constant for the id tag
		 */
		private static final String ID_ATTRIBUTE = "id";

		/**
		 * the constant for the start tag
		 */
		private static final String START_DATE_TAG = "start";
		
		/**
		 * the constant for the end tag
		 */
		private static final String END_DATE_TAG = "end";

		/**
		 * Handles opening tags.
		 */
		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			if (localName.equals(POI_TAG)) {
				mCurrentPoi = new PoiWrapper();
				mCurrentPoi.setUri(attributes.getValue(ID_ATTRIBUTE));
			} else if (localName.equals(CATEGORY_TAG)) {
				mCurrentCategory = new CategoryWrapper();
				mCurrentCategory.setUri(attributes.getValue(ID_ATTRIBUTE));
			}
		}

		/**
		 * Handles closing tags.
		 */
		@Override
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			if (localName.equals(POI_TAG)) {
				mPois.add(mCurrentPoi);
				mCurrentPoi = null;
			} else if (localName.equals(CATEGORY_TAG)) {
				mCurrentPoi.addCategory(mCurrentCategory);
				mCurrentCategory = null;
			}

			if (mCurrentPoi != null) {
				if (mCurrentCategory == null) {
					if (localName.equals(NAME_TAG)) {
						mCurrentPoi.setName(mCurrentCharacters);
					} else if (localName.equals(LAT_TAG)) {
						mCurrentPoi.setLatitude(Double
								.valueOf(mCurrentCharacters));
					} else if (localName.equals(LONG_TAG)) {
						mCurrentPoi.setLongitude(Double
								.valueOf(mCurrentCharacters));
					} else if (localName.equals(ALT_TAG)) {
						mCurrentPoi.setAltitude(Double
								.valueOf(mCurrentCharacters));
					} else if (localName.equals(START_DATE_TAG)) {
						mCurrentPoi.setStartDate(mCurrentCharacters);
					} else if (localName.equals(END_DATE_TAG)) {
						mCurrentPoi.setEndDate(mCurrentCharacters);
					}
				} else {
					if (localName.equals(NAME_TAG)) {
						mCurrentCategory.setName(mCurrentCharacters);
					}
				}
			}
		}

		/**
		 * Handles characters of tags.
		 */
		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {
			mCurrentCharacters = new String(ch, start, length);
		}

		/**
		 * Gets the list of matching POIs.
		 * 
		 * @return the list of matching POIs
		 */
		public List<PoiWrapper> getList() {
			return mPois;
		}
	}

}
